#ifndef SIMPLE_IO_UDP_SOCKET_H
#define SIMPLE_IO_UDP_SOCKET_H

#include <cstddef> // std::byte
#include <variant> // std::variant

#include "simple/support/meta/pend.hpp" // support::meta::prepend_t

#include "error.h" // error
#include "address.h" // address
#include "handle.h" // unique_hanlde
#include "open_execute_common.h" // access_denied process_descriptor_limit_reached system_descriptor_limit_reached
#include "write_socket_common.h" // network_unreachable network_interface_down
#include "byte_view.hpp" // byte_view
#include "byte_range.hpp" // byte_range

namespace simple::io
{
	namespace meta = support::meta;

	struct address_in_use : error {};
	struct address_not_available : error {};

	using socket_error_t = std::variant<
		access_denied,
		address_in_use,
		address_not_available,
		process_descriptor_limit_reached,
		system_descriptor_limit_reached,
		error
	>;

	struct udp_socket
	{
		unique_handle handle;
	};

	using socket_result = meta::prepend_t<socket_error_t, udp_socket>;

	[[nodiscard]] socket_result open_udp_socket() noexcept;


	struct address_not_supported : error {};
	struct connection_reset : error {};
	struct message_too_long : error {};
	struct host_unreachable : error {};

	using send_error_t = std::variant<
		access_denied,
		address_not_supported,
		connection_reset,
		message_too_long,
		host_unreachable,
		network_interface_down,
		network_unreachable,
		error
	>;

	using send_result = meta::prepend_t<socket_error_t, const std::byte*>;
	[[nodiscard]] send_result send(const udp_socket&, const address&, byte_view message) noexcept;


	using receive_error_t = std::variant<
		connection_reset,
		error
	>;
	using receive_result = meta::prepend_t<receive_error_t, std::byte*>;
	[[nodiscard]] receive_result receive(const udp_socket&, byte_range) noexcept;

} // namespace simple::io

#endif /* end of include guard */
